package com.ra.landlord;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.manager.PersonalizedDictionaryService;
import com.espirit.eap.manager.TaskScheduler;
import com.espirit.eap.manager.TaskSchedulerService;
import com.espirit.eap.sa.LoginUser;
import com.espirit.eap.sa.LoginUserService;
import com.espirit.weixin.core.WeixinUtil;
import com.googlecode.cswish.annotation.Json;
import com.googlecode.cswish.annotation.Protected;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.PageItemMatcher;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.Condition;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.Initialization;
import com.googlecode.cswish.struts.spring.BeanIntrospector;
import com.googlecode.cswish.struts.spring.Executor;
import com.googlecode.cswish.struts.spring.GenericJPAService;
import com.googlecode.cswish.struts.spring.ModelPropertyInfo;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.ClassUtil;
import com.googlecode.cswish.util.bean.ModelUtil;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.Result;
import com.ra.external.FeeNotificationService;
import com.ra.external.FinanceStatementService;
import com.ra.external.RoomInfoService;
import com.ra.external.RoomRetrievalService;
import com.ra.external.WeixinService;
import com.ra.landlord.constant.FeeRuleType;
import com.ra.landlord.constant.LandlordConstant;
import com.ra.order.rental.RentalOrder;
import com.ra.order.rental.RentalOrderService;
import com.ra.system.Version;
import com.ra.util.Assert;
import com.ra.util.DateTimeUtil;
import com.ra.util.QRCodeService;
import com.ra.util.RaConstant;

@Service
public class RoomService implements Initialization {

	private static final Log logger = LogFactory.getLog(RoomService.class);
	
	@Resource
	private GenericJPAService genericService;
	
	@Resource
	private FeeService feeService;
	
	@Resource
	private LandlordUserService landlordUserService;
	
	@Resource
	private LoginUserService loginUserService;
	
	@Resource
	private AgreementService agreementService;
	
	@Resource
	private MeterService meterService;
	
	@Resource
	private RoomRetrievalService retrievalService;
	
	@Resource
	private RoomInfoService roomInfoService;
	
	@Resource
	private WeixinUtil weixinUtil;
	
	@Resource
	private QRCodeService qrCodeService;
	
	@Resource
	private RentalOrderService rentalOrderService;
	
	@Resource
	private TaskSchedulerService taskSchedulerService;
	
	@Resource
	private PersonalizedDictionaryService personalizedDictionaryService;
	
	@Resource
	private FeeRecordService feeRecordService;
	
	@Resource
	private FinanceStatementService financeStatementService;
	
	@Resource
	private FeeNotificationService notificationService;
	
	@Resource
	private BeanIntrospector beanIntrospector;
	
	@PersistenceContext(unitName="espirit")
	protected EntityManager em;
	
	public static int RENT_REMINDER_DAY_COUNTS = 7; 

	private RoomService roomService;
	
	private RoomService getThis() {
		if (roomService == null) {
			roomService = ObjectFactory.getInstance().getBean(RoomService.class, false);
		}
		return roomService;
	}
	
	@Override
	public String[] getProductName() {
		return RaConstant.PRODUCT_NAME;
	}
	
	@Override
	public void initSite() {
		// 检查和启用费用计算JOB
		TaskScheduler model = new TaskScheduler();
		model.setName("房屋费用计算");
		model.setActive(true);
		model.setCommand(this.getClass().getName() + ".updateRoomFee");
		model.setCond("0 0 1 * * ?");			// 每晚1点
		model.setDescription("定期更新房屋的本次应收费用, 上次实收费用");
		//taskSchedulerService.checkAndAdd(model);
	}
	
	/**
	 * 给指定的用户ID生成微信公共号的关注二维码, 用户关注此二维码后, 微信服务器将发给我们一条用户的响应信息
	 * 用户微信将绑定某个租客
	 * 
	 * @param loginUserId
	 * @param width
	 * @param height
	 * @return
	 * @throws Exception
	 */
	public Result showPublicAccount(String loginUserId, int width, int height) throws Exception {
		// LU means login user
		String url = weixinUtil.createQRCode(WeixinService.SUBSCRIBE_KEY_LOGINUSER, new Integer(loginUserId), null, null);
		return qrCodeService.show(url, width, height);
	}
	
	@Transactional
	@Rest(type="add")
	public Room add(Room model){
		
		model.judgeDoAdapt();
		fillRoomConfig(model);
		Room newRoom = genericService.add(model);
		if (newRoom.getBuilding()!=null) {
			retrievalService.publish(newRoom, false);
		}
		return newRoom;
	}
	
	@Transactional
	@Rest(type="add")
	public Room addRoomBatch(Room model){
		LandlordUser landlordUser = landlordUserService.getLoginUser();
		model.setLandlordUser(landlordUser.getOwnerUser());
		//默认为未出租1,其他状态（已出租：0；预定：2）
		model.setRentState(LandlordConstant.RENT_STATE_NOYET);
		
		Building building = genericService.load(model.getBuilding());
		model.setAddress(building.getAddress());
		//PictureNo设置，暂时和PC保持一致
		model.setPictureNo(1);
		model.setNo(model.getName());
		
		model.judgeDoAdapt();
		fillRoomConfig(model);
		// 默认整租
		model.setRentType(Room.RENT_TYPE_ENTIRE);
		Room room = genericService.merge(model);
		
		// 修改对应的room_count值
		building.setRoomCount(building.getRoomCount() + 1);
		// 默认整租
		building.setRentType(Building.RENT_TYPE_ENTIRE);
		genericService.merge(building);
		
		try {
			
			//如果需要推广，则自动发布推广
			if (room.getBuilding() != null) {
				retrievalService.publish(room, false);
			}
		} catch (Exception e) {
			logger.error("room publish is error!",e);
		}
		
		return room;
		
	}
	
	@Transactional
	public Room addRoomBatchNotPublish(Room model) {
		LandlordUser landlordUser = landlordUserService.getLoginUser();
		model.setLandlordUser(landlordUser.getOwnerUser());
		//默认为未出租1,其他状态（已出租：0；预定：2）
		model.setRentState(LandlordConstant.RENT_STATE_NOYET);
		
		Building building = genericService.load(model.getBuilding());
		model.setAddress(building.getAddress());
		//PictureNo设置，暂时和PC保持一致
		model.setPictureNo(1);
		model.setNo(model.getName());
		
		model.judgeDoAdapt();
		fillRoomConfig(model);
		// 默认整租
		model.setRentType(Room.RENT_TYPE_ENTIRE);
		Room room = genericService.merge(model);
		
		// 修改对应的room_count值
		building.setRoomCount(building.getRoomCount() + 1);
		// 默认整租
		building.setRentType(Building.RENT_TYPE_ENTIRE);
		genericService.merge(building);
		return room;
	}
	
	// FIXME: 此方法有安全问题， 待讨论 (冯展 2015-2-13)
	@Transactional
	@Rest(type="add")
	public Room addRoom(Room model){
		// 先确保room的事务提交
		Room room = genericService.executeInNewTx(new Executor<Room, Room>() {

			@Override
			public Room execute(Room model) {
				LandlordUser landlordUser = landlordUserService.getLoginUser();
				model.setLandlordUser(landlordUser.getOwnerUser());
				//默认为未出租1,其他状态（已出租：0；预定：2）
				model.setRentState(LandlordConstant.RENT_STATE_NOYET);
				
				List<FeeRule> lstFeeRule = model.getFeeRules();
				if (lstFeeRule != null) {
					for (FeeRule feeRule : lstFeeRule) {
						if (feeRule.getFeeRuleCfg() != null) {
							FeeRuleCfg feeRuleCfg = genericService.load(feeRule.getFeeRuleCfg());
							feeRule.setFeeRuleCfg(feeRuleCfg);
							feeRule.setRoom(model);
						}
					}
				}
				
				List<RoomItem> lstRoomItem = model.getRoomItems();
				if (lstRoomItem != null) {
					for (RoomItem roomItem : lstRoomItem) {
						if (roomItem.getRoomItemCfg() != null) {
							RoomItemCfg roomItemCfg = genericService.load(roomItem.getRoomItemCfg());
							roomItem.setRoomItemCfg(roomItemCfg);
							roomItem.setRoom(model);
						}
					}
				}
				
				Building building = genericService.load(model.getBuilding());
				model.setAddress(building.getAddress());
				//PictureNo设置，暂时和PC保持一致
				model.setPictureNo(1);
				model.setNo(model.getName());
				
				// 附件
				if (model.getRoomAttachments() != null) {
					for (RoomAttachment roomAttachment : model.getRoomAttachments() ) {
						if (roomAttachment != null) {
							roomAttachment.setRoom(model);
						}
					}
				}
				
				//TODO  zpx 2015-08-29 待分租业务完善过后，再根据页面创建情况来决定是整租还是分租类型。现默认是整租
				
				model.setRentType(Room.RENT_TYPE_ENTIRE);
				model.judgeDoAdapt();
				fillRoomConfig(model);
				Room room = genericService.merge(model);
				
				// 修改对应的room_count值
				building.setRoomCount(building.getRoomCount() + 1);
				genericService.merge(building);
				return room;
			}
			
		}, model);
		
		try {
			//临时方案，延时1秒
			Thread.sleep(1000);
			
			//如果需要推广，则自动发布推广
			if (room.getBuilding() != null) {
				retrievalService.publish(room, false);
			}
		} catch (Exception e) {
			logger.error("room publish is error!",e);
		}
		
		return room;
	}
	
	//同步roomdesc和roomconfigdesc
	public void fillRoomConfig(Room model) {
		RoomConfig roomConfig = model.getRoomConfig();
		int config = 0;
		if (roomConfig != null) {
			//同步roomconfig到roomconfigDesc
			if (Boolean.TRUE.equals(roomConfig.getBalcony())) {
				config += 1;
			}
			if (Boolean.TRUE.equals(roomConfig.getKitchen())) {
				config += 1<<1;
			}
			if (Boolean.TRUE.equals(roomConfig.getToilet())) {
				config += 1<<2;
			}
			if (Boolean.TRUE.equals(roomConfig.getAirCondition())) {
				config += 1<<3;
			}
			if (Boolean.TRUE.equals(roomConfig.getDesk())) {
				config += 1<<4;
			}
			if (Boolean.TRUE.equals(roomConfig.getBroadband())) {
				config += 1<<5;
			}
			if (Boolean.TRUE.equals(roomConfig.getWashingMachine())) {
				config += 1<<6;
			}
			if (Boolean.TRUE.equals(roomConfig.getTv())) {
				config += 1<<7;
			}
			if (Boolean.TRUE.equals(roomConfig.getBed())) {
				config += 1<<8;
			}
			if (Boolean.TRUE.equals(roomConfig.getFreezer())) {
				config += 1<<9;
			}
			if (Boolean.TRUE.equals(roomConfig.getHeater())) {
				config += 1<<10;
			}
			model.setConfig(config);
			
		} else {
			//同步roomconfigDesc到roomconfig
			roomConfig = new RoomConfig();
			model.setRoomConfig(roomConfig);
			if(model.getConfig() == null) {
				return;
			}
			config = model.getConfig();
			if ( (config & 1) == 1) {
				roomConfig.setBalcony(true);
			}
			if ( (config & (1 << 1)) > 0) {
				roomConfig.setKitchen(true);
			}
			if ( (config & (1 << 2)) > 0) {
				roomConfig.setToilet(true);
			}
			if ( (config & (1 << 3)) > 0) {
				roomConfig.setAirCondition(true);
			}
			if ( (config & (1 << 4)) > 0) {
				roomConfig.setDesk(true);
			}
			if ( (config & (1 << 5)) > 0) {
				roomConfig.setBroadband(true);
			}
			if ( (config & (1 << 6)) > 0) {
				roomConfig.setWashingMachine(true);
			}
			if ( (config & (1 << 7)) > 0) {
				roomConfig.setTv(true);
			}
			if ( (config & (1 << 8)) > 0) {
				roomConfig.setBed(true);
			}
			if ( (config & (1 << 9)) > 0) {
				roomConfig.setFreezer(true);
			}
			if ( (config & (1 << 10)) > 0) {
				roomConfig.setHeater(true);
			}
		}
	}
	
	/**
	 * 更新房间
	 * 
	 * @param model
	 * @return
	 */
	@Transactional
	@Rest(type = "update")
	public Room updateRoom(Room model){

		Collection<String> enableFields = Arrays.asList("name", "roomType", "purpose", "area", "buildingFloor", "roomConfigDesc"
				, "roomItems", "roomItems.roomItemCfg", "feeRules", "roomAttachments", "roomConfig");
		
		model.judgeDoAdapt();
		fillRoomConfig(model);
		Room room = genericService.update(model, false, enableFields, null);
		genericService.merge(model.getRoomConfig());
		
		//修改房间后，将费用对应的房间的feerecord删除
		feeRecordService.repairedFeeRecordByRoom(room);
		
		try {
			//如果需要推广，则自动发布推广
			if (room.getBuilding() != null) {
				retrievalService.publish(room, false);
			}
		} catch (Exception e) {
			logger.error("room publish is error after update room!",e);
		}
		
		return room;
		
		
//		//landlordUserService.getLoginUser()
//		Room room = genericService.load(model);
//		room.setName(model.getName());
//		room.setRoomType(model.getRoomType());
//		room.setPurpose(model.getPurpose());
//		room.setArea(model.getArea());
//		room.setBuildingFloor(model.getBuildingFloor());
//		room.setRoomConfigDesc(model.getRoomConfigDesc());
//		
//		// 房间资源修改
//		List<RoomItem> oldRoomItems = room.getRoomItems();
//		List<RoomItem> modelRoomItems = model.getRoomItems();
//		oldRoomItems.clear();
//		if (modelRoomItems != null) {
//			for (RoomItem modelRoomItem : modelRoomItems) {
//				if (modelRoomItem.getId() != null) {
//					modelRoomItem = genericService.load(modelRoomItem);
//				}else if(modelRoomItem.getRoomItemCfg().getId() != null){
//					RoomItemCfg roomItemCfg = genericService.load(modelRoomItem.getRoomItemCfg());
//					modelRoomItem.setRoomItemCfg(roomItemCfg);
//					modelRoomItem.setRoom(model);
//				}
//			}
//			oldRoomItems.addAll(modelRoomItems);
//		}
//		
//		//房间收费规则修改
//		List<FeeRule> oldFeeRules = room.getFeeRules();
//		List<FeeRule> modelFeeRules = model.getFeeRules();
//		oldFeeRules.clear();
//		if (modelFeeRules != null) {
//			for (FeeRule modelFeeRule : modelFeeRules) {
//				if (modelFeeRule.getId() != null) {
//					modelFeeRule = genericService.load(modelFeeRule);
//					oldFeeRules.add(modelFeeRule);
//				} else if(modelFeeRule.getFeeRuleCfg().getId() != null){
//					FeeRuleCfg feeRuleCfg = genericService.load(modelFeeRule.getFeeRuleCfg());
//					modelFeeRule.setFeeRuleCfg(feeRuleCfg);
//					modelFeeRule.setRoom(model);
//					oldFeeRules.add(modelFeeRule);
//				} else {
//					// skip it
//				}
//		}
//		}
//		
//		// 附件
//		Collection<String> enableProperties = Arrays.asList("roomAttachments");
//		ModelUtil.deepUpdate(model, room, null, enableProperties, enableProperties);
//		if (room.getRoomAttachments() != null) {
//			for (RoomAttachment roomAttachment : room.getRoomAttachments() ) {
//				roomAttachment.setRoom(room);
//			}
//		}
//		return genericService.merge(room);
	}
	
	/**
	 * 删除房间
	 * 需要更新楼房的房间数（待做）
	 * @param rooms
	 * @return
	 */
	@Rest(type = "delete")
	public boolean deleteRoom(List<Room> rooms){
		
		List<Object> willDeleteList = new ArrayList<Object>(rooms.size());
		for (Object item : rooms) {
			
			Room room = (Room)item;
			
			//只有未出租的才能删除
			Room dbRoom = genericService.load(Room.class, room.getId());
			if (!Room.RENT_STATE_NOYET.equals(dbRoom.getRentState()))
			{
				continue;
			}
			
			willDeleteList.add(room);
			
			//关闭room对应的订单
			feeRecordService.closeFeeRecordAndOrderByRoom(room);
		}
		
		if (willDeleteList.isEmpty())
		{
			throw new ApplicationException("房间删除失败, 只能删除未出租房间！");
		}
		
		boolean result = false;
		result = genericService.deleteList(willDeleteList);
		
		if (!result) {
			throw new ApplicationException("房间删除失败");
		}
		
		//删除room对应发布的roominfo
		for (Object r : willDeleteList) 
		{
			Room deletedRoom = (Room)r;
			roomInfoService.deleteRoomInfoByRoom(deletedRoom.getId());
		}
		
		return result;
	}
	
	@Rest(type = "search")
	public PageInfo<Room> search(Room model, int start, int limit,
    		String sort, String dir, boolean oldSearch, Reference<QLInfo> qlInfoRef) {
		model.setStatus(Version.STATUS_VALID);
		// 已出租: model.getAgreement() != null
		// 已交租: model.getFees().size() > 0 && model.getFees().get(0).getAmountActual() != null
		PageInfo<Room> pageInfo = genericService.search(model, start, limit, sort, dir, oldSearch, qlInfoRef);
		
		initFeeInfo(pageInfo.getData());
		
		return pageInfo;
    }
	
	/**
	 * 查询房东需要提醒的收租费用
	 * @param model
	 * @param start
	 * @param limit
	 * @param sort
	 * @param dir
	 * @param oldSearch
	 * @param qlInfoRef
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchWithFeeReminder(Room model, int start, int limit,
    		String sort, String dir, boolean oldSearch, Reference<QLInfo> qlInfoRef) {
		model.setStatus(Version.STATUS_VALID);
		List<Room> lstRoom = genericService.search(model, start, limit, sort, dir, oldSearch, qlInfoRef).getData();
		
		List<Room> lstPayRoom = new ArrayList<>();
		List<Room> lstNoPayRoom = new ArrayList<>();
		
		if (lstRoom != null) {
			Calendar calendar = Calendar.getInstance();
			String today=DateTimeUtil.formatDate(calendar.getTime());
			LoginUser loginUser = loginUserService.getLoginUser();
			
			// 获取用户设置的提前提醒天数，如果未设置，默认为提前7天
			int dayCount = getRentReminderDayCounts(loginUser);
			calendar.add(Calendar.DATE, dayCount);
			
			for (Room room : lstRoom) {
				Agreement validAgreement = room.getAgreement();
				if(validAgreement != null){
					
					// 临时逻辑，因PC端部分数据room.rental为空，故在此做数据补全，将合同的租金补全到room
					if(StringUtils.isBlank(room.getRental()) && validAgreement.getRentPay() != null){
						room.setRental(validAgreement.getRentPay().toString());
					}
					
					List<Fee> lstFee = validAgreement.getFees();
					if (lstFee == null || lstFee.size() == 0) {
						// 预定房间的也放在未收租里
						if (room.getRentState() == LandlordConstant.RENT_STATE_BOOK) {
							room.setHasPay(false);
							lstNoPayRoom.add(room);
						}
						continue;
					}else if(lstFee.size() == 1){
						room.setHasPay(false);
						lstNoPayRoom.add(room);
						continue;
					}else if(lstFee.size() > 1){
						if (lstFee.get(0).getDateEndRecv().compareTo(today) < 0) {
							room.setHasPay(false);
							lstNoPayRoom.add(room);
							continue;
						}
						
						String reminderDate = DateTimeUtil.formatDate(calendar.getTime());
						if (lstFee.get(0).getDateEndRecv().compareTo(reminderDate) < 0) {
							room.setHasPay(true);
							lstPayRoom.add(room);
						}
					}
					
				}
			}
		}
		
		List<Room> rooms = initFeeInfo(lstNoPayRoom);
		List<Room> hasPayRooms = new ArrayList<>();
		if (lstPayRoom.size() > 0) {
			for (Room room : lstPayRoom) {
//				List<Room> tempRooms = feeService.calculateNextFee(room.getId());
//				hasPayRooms.addAll(tempRooms);
				
				Fee receivableFee = getFeeByFeeRecord(room, true);
				room.setReceivableFee(receivableFee);
				hasPayRooms.add(room);
			}
		}
		if (hasPayRooms.size() > 0) {
			rooms.addAll(hasPayRooms);
		}
		return rooms;
		
	}
	
	/**
	 * 获取用户设置的提前提醒天数，如果未设置，默认为提前7天
	 * @param loginUser
	 * @return
	 */
	private int getRentReminderDayCounts(LoginUser loginUser){
		int dayCount = RENT_REMINDER_DAY_COUNTS;
		String strDayCount = personalizedDictionaryService.getValueByTypeName(loginUser, "rentReminderDayCounts", "收租提醒提前天数");
		if (StringUtils.isNotBlank(strDayCount)) {
			try {
				dayCount = Integer.parseInt(strDayCount);
			} catch (NumberFormatException e) {
				logger.warn("从用户个性化数据字典里获取收租提醒提前天数的值转化数字出现异常，转化的值为：" + strDayCount,e);
			}
		}
		return dayCount;
	}
	
	// FIXME: 待替换searchWithFee
	@Rest(type = "search")
	//@Json("")
	public PageInfo<Room> searchFeeByBuilding(final Room model, int start, int limit, String sort, String dir) {
		//model.hasRent:true
		//model.hasPay:false
		//model.building.id:1
		Building dbBuilding = genericService.load(Building.class, model.getBuilding().getId());
		PageInfo<Room> pageInfo = PageInfo.convertToPageInfoByIndex(dbBuilding.getRooms(), new PageItemMatcher<Room, Room>() {

			@Override
			public boolean matched(Room room) {
				boolean match = true;
				if (model.getHasPay() != null) {
					// initialize pay status
					if (Room.RENT_TYPE_ENTIRE.equals(room.getRentType())) {
						if (room.getAgreement() != null) {
							if (room.getAgreement().getId() == null || room.getAgreement().getFees().size() <= 1 
									|| DateTimeUtil.GetDateWithAdvanceNum(DateTimeUtil.StrToDate(room.getAgreement().getFees().get(0).getDateEndRecv()), 
											room.getAgreement().getAdvanceNum()).compareTo(DateTimeUtil.throwTimeForDate(new Date())) < 0) {
								room.setHasPay(false);
							} else {
								room.setHasPay(true);
							}
						} else {
							room.setHasPay(false);
						}
					} else {
						//TODO: subRoom
					}
					
					// check condition 'hasPay'
					if (!model.getHasPay().equals(room.getHasPay())) {
						match = false;
					}
				}
				
				if (match && model.getHasRent() != null) {
					// check condition 'hasRent'
				}
				return match;
			}

			@Override
			public Room toResult(Room object) {
				return object;
			}
		}, start, limit);
		return pageInfo;
	}
	
	
	@Rest(type = "search")
	public List<Room> searchWithFee(Room model, int start, int limit, String sort, String dir) {
		// 设置楼层查询条件的
		BuildingFloor buildingFloor = new BuildingFloor();
		if (model.getBuildingFloor() != null && model.getBuildingFloor().getId() != null) {
			buildingFloor.setId(model.getBuildingFloor().getId());
		}else{
			buildingFloor.setId(0);
		}
		model.setBuildingFloor(buildingFloor);
		
		Integer chargState = null;
		if (model.getHasPay() != null) {
			chargState = model.getHasPay() ? 0 : 1;
		}
		
		List<Room> rooms = searchRoomsNew(model, null, chargState, null).getData();
		rooms = sortAndPageList(rooms, sort, dir, start, limit);
		
		List<Room> newRooms = new ArrayList<Room>();
		if (rooms != null) {
			for (Room room : rooms) {
				if (room.getRentState() == Room.RENT_STATE_ALREADY || room.getRentState() == Room.RENT_STATE_BOOK) {
					room = _collectRental(room);
					
					//已收租的情况下是否需要显示押金
					if(room.getHasPay()&&room.getAgreement()!=null && room.getAgreement().getFees().size()==2 && !isDirtyData(room.getAgreement().getFees())){
						room.setNeedAddDeposit(true);
						//租金
						Fee	fee = room.getAgreement().getFees().get(0);
						//押金
						Fee yj = room.getAgreement().getFees().get(1);
						BigDecimal sumActual = fee.getAmountActual().add(yj.getAmountActual());
						BigDecimal sumRecv = fee.getAmountRecv().add(yj.getAmountActual());
						fee.setAmountActual(sumActual);
						fee.setAmountRecv(sumRecv);
						fee.setDepositActual(yj.getAmountActual());
						room.setReceivableFee(fee);
					}
					
					//如果需要显示器押金，后端费用计算，前端显示
					if(room.getNeedAddDeposit()!=null &&room.getNeedAddDeposit().booleanValue()){
						Fee receivableFee = room.getReceivableFee();
						BigDecimal deposit = room.getDeposit();
						if(deposit==null){
							try{
								String deposit2 =room.getAgreement().getDeposit();
								deposit = new BigDecimal(deposit2).setScale(2,RoundingMode.HALF_UP);
							}catch(Exception e){
								deposit = new BigDecimal(0);
							}
						}
						BigDecimal amountActual = receivableFee.getAmountActual();
						BigDecimal amountRecv = receivableFee.getAmountRecv();
						if(amountActual==null){
							amountRecv = amountRecv.add(deposit);
						}
						amountActual = amountActual==null?amountRecv:amountActual;
						receivableFee.setAmountActual(amountActual);
						receivableFee.setAmountRecv(amountRecv);
					}
					
					List<FeeAddition> feeAdditions = room.getReceivableFee().getFeeAdditions();
					if(CollectionUtils.isEmpty(feeAdditions)) {
						room.getReceivableFee().setFeeAdditions(new ArrayList<FeeAddition>());
					}
					newRooms.add(room);
				}
			}
		}
		return newRooms;
		
    }

	/**
	 * 如果两条交租的费用时间不一说明是脏数据，对于脏数据不显示押金
	 * @param fees
	 * @return
	 */
	private boolean isDirtyData(List<Fee> fees){
		if(fees.size()!=2)return false;
		String fz = fees.get(0).getDateActual();
		String yj = fees.get(1).getDateActual();
		if(!StringUtils.isBlank(fz)&&!StringUtils.isBlank(yj)){
			Date date1 = DateTimeUtil.StrToDate(fz);
			Date date2 = DateTimeUtil.StrToDate(yj);
			int compareTo = date1.compareTo(date2);
			if(compareTo==0){
				return false;
			}else{
				return true;
			}
		}else{
			return true;
		}
	}
	
	private <T> List<T> sortAndPageList(List<T> rooms, String sort, String dir, int start, int limit) {
		if (rooms == null || rooms.isEmpty()) {
			return rooms;
		}
		
		if (sort != null && dir != null) {
			// TODO: group sort
			final ModelPropertyInfo mp = beanIntrospector.getModelPropertyInfo(ClassUtil.getClassName(rooms.get(0)), sort);
			final boolean isAsc = "asc".equalsIgnoreCase(dir) || "csa".equalsIgnoreCase(dir);
			final boolean isReverse = "csa".equalsIgnoreCase(dir) || "csed".equalsIgnoreCase(dir);
			if (mp != null && mp.propertyInfo != null) {
				Collections.sort(rooms, new Comparator<T>() {
					@Override
					public int compare(T o1, T o2) {
						Object v1 = mp.propertyInfo.getValue(o1, mp.modelInfo);
						Object v2 = mp.propertyInfo.getValue(o2, mp.modelInfo);
						
						if (v1 instanceof String && isReverse) {
							// 高低位反转
							String str1 = (String)v1;
							int mid1 = str1.length() / 2;
							v1 = str1.substring(mid1, str1.length()) + '-' + str1.substring(0, mid1);
							String str2 = (String)v2;
							int mid2 = str2.length() / 2;
							v2 = str2.substring(mid2, str2.length()) + '-' + str1.substring(0, mid2);
						}
						
						int ret;
						if (v1 instanceof String && !isReverse && NumberUtils.isDigits((String)v1) && NumberUtils.isDigits((String)v2)) {
							ret = new Integer((String)v1).compareTo(new Integer((String)v2));
						} else {
							ret = ObjectUtils.compare((Comparable<?>)v1, (Comparable<?>)v2);
						}
						return isAsc ? ret : -ret;
					}
				});
			}
		}
		
		if (start >= 0 && limit > 0) {
			int toIndex = start + limit;
			if (toIndex > rooms.size()) {
				toIndex = rooms.size();
			}
			rooms = rooms.subList(start, toIndex);
		}
		return rooms;
	}
	
	/**
	 * 初始化费用数据
	 * 
	 * @param roomSource
	 * @return
	 */
	private List<Room> initFeeInfo(List<Room> roomSource) {
		List<Room> rooms = new ArrayList<Room>();
		if (roomSource != null) {
			for (Room room : roomSource) {
				if (room.getAgreement() != null) {
					updateSingleRoomFee(room, false);
				}
				
				rooms.add(room);
			}
		}
		return rooms;
	}
	
	/**
	 * 判断并计算房间费用, 并将结果存到DB 
	 * 
	 * @param dbRoom 来自db的room记录
	 * @param forceUpdate 强制重新计算,并更新结果
	 */
	@Protected
	public void updateSingleRoomFee(Room dbRoom, boolean forceUpdate) {
		boolean changed = false;
		
		if (dbRoom.getReceivableFee() == null || forceUpdate) {
			
			//Fee receivableFee = feeService.calculateFee(dbRoom);
			Fee receivableFee = getFeeByFeeRecord(dbRoom, false);
			
			dbRoom.setReceivableFee(receivableFee);
			changed = true;
		}
		
		if (dbRoom.getLastFee() == null || forceUpdate) {
			List<Fee> fees = dbRoom.getAgreement().getFees();
			if (fees != null && fees.size() > 1) {
				boolean hasPay = dbRoom.getHasPay() != null && dbRoom.getHasPay();
				if(hasPay && fees.size() > 2){
					dbRoom.setLastFee(fees.get(1));
				}
				if(!hasPay){
					dbRoom.setLastFee(fees.get(0));
				}
			}
		}
		
		if (changed) {
			//genericService.merge(dbRoom);
		}
	}
	
	/**
	 * 通过从FeeRecord获取Fee
	 * @param dbRoom
	 * @param isNextFee
	 * @return
	 */
	private Fee getFeeByFeeRecord(Room dbRoom, boolean isNextFee){
		
		Fee receivableFee = null;
		FeeRecord feeRecord = null;
		//1、读取该房间是否有未收租的FeeRecord
		FeeRecord modelFeeRecord = new FeeRecord();
		modelFeeRecord.setRoom(dbRoom);
		modelFeeRecord.setStatus(FeeRecord.FEE_STATUS_RENT_TODO);
		
		List<FeeRecord> feeRecords = feeRecordService.searchFeeRecord(modelFeeRecord);
		
		if (feeRecords != null && feeRecords.size() > 0) {
			// 如果存在未缴费用，获取feeRecord，并转为receivableFee
			receivableFee = new Fee();
			feeRecord = feeRecords.get(0);
			feeRecordService.copyFeeRecord2Fee(feeRecord, receivableFee);
			receivableFee.setId(null);
			receivableFee.setFeeRecordId(feeRecord.getId());
		} else {
			// 如果不存在未缴费用，则临时计算后，持久化feeRecord，并转为receivableFee
			feeRecord = new FeeRecord();
			if (isNextFee) {
				List<Fee> fees = dbRoom.getFees();
				if (fees != null && fees.size() > 0) {
					// room中的fees没有倒序排序，所以fees.get(fees.size()-1)
					receivableFee = feeService.calculateNewFee(fees.get(fees.size()-1), dbRoom);
				}
			}else{
				receivableFee = feeService.calculateFee(dbRoom);
			}
			if (receivableFee != null) {
				receivableFee.setRoom(dbRoom);
				if (dbRoom.getHasPay() != null && dbRoom.getHasPay()) {
					//待确定
					receivableFee.setStatus(FeeRecord.FEE_STATUS_RENT_DONE);
					//查询对应的feeRecord,条件agreement.id,begintime,endtime
					feeRecord.setAgreement(receivableFee.getAgreement());
					feeRecord.setDateBeginRecv(receivableFee.getDateBeginRecv());
					feeRecord.setDateEndRecv(receivableFee.getDateEndRecv());
					List<FeeRecord> tempFeeRecords = genericService.searchAll(feeRecord).getData();
					if (tempFeeRecords != null && tempFeeRecords.size() > 0) {
						feeRecord = tempFeeRecords.get(0);
					}
				}else{
					receivableFee.setStatus(FeeRecord.FEE_STATUS_RENT_TODO);
					feeRecordService.copyFee2FeeRecord(receivableFee, feeRecord);
					//保存至FeeRecord
					feeRecord = feeRecordService.updateFeeRecord(feeRecord);
				}
			}
		}
		
		//设置是否有网上收租单、上传的收租凭证、支付方式
		if (receivableFee != null && feeRecord != null && feeRecord.getId() != null) {
			//获取订单中的paymode,RentalVoucherFileName
			RentalOrder rentalOrder = feeRecordService.searchRentalOrderByFee(feeRecord);
			if (rentalOrder != null) {
				if (receivableFee.getPayMode() == null) {
					receivableFee.setPayMode(rentalOrder.getRentalVoucherPayType());
				}
				receivableFee.setRentalVoucherFileName(rentalOrder.getRentalVoucherFileName());
				receivableFee.setHasRentalOrder(true);
			}else{
				receivableFee.setHasRentalOrder(false);
			}
			receivableFee.setFeeRecordId(feeRecord.getId());
		}
		
		return receivableFee;
	}
	
	/**
	 * 出租到期提醒
	 * 
	 * @param buildingId
	 * @type type 1:合约过期；2：未来1月即将过期
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchWithExpirationReminder(Integer buildingId, Integer type) {
		
		Room model = new Room();
		LandlordUser landlordUser = landlordUserService.getLoginUser().getOwnerUser();
		ModelUtil.setProperty(model, "landlordUser.id", landlordUser.getId());
		ModelUtil.setProperty(model, "building.id", buildingId);
		ModelUtil.setProperty(model, "hasRent", true);
		model.setStatus(Version.STATUS_VALID);
		
		PageInfo<Room> pageInfo = genericService.searchAll(model);
		
		List<Room> rooms = new ArrayList<Room>();
		Calendar calendar = Calendar.getInstance();
		String strToday = DateTimeUtil.formatDate(calendar.getTime());
		calendar.add(Calendar.DATE, 30);
		String strRemindTime = DateTimeUtil.formatDate(calendar.getTime());
		for (Room room : pageInfo.getData()) {
			room.setHasRent(true);
			ValidAgreement agreement = room.getAgreement();
			if (agreement != null) {
				if (type != null) {
					if (type.equals(1)) {
						if (agreement.getRentEndDate().compareTo(strToday) < 0) {
							rooms.add(room);
						}
					}else if(type.equals(2)){
						if (agreement.getRentEndDate().compareTo(strToday) > 0 && agreement.getRentEndDate().compareTo(strRemindTime) < 0) {
							rooms.add(room);
						}
					}
				}else{
					if (agreement.getRentEndDate().compareTo(strRemindTime) < 0) {
						rooms.add(room);
					}
				}
			}
		}
		
		return rooms;
	}
	
	/**
	 * 出租，查询已出租、未出租
	 * (注：boolean hasRent 需要改为  Boolean hasRent)
	 * @param buildingId
	 * @param hasRent
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchWithRental(Integer buildingId, boolean hasRent, boolean isAll, int start, int limit, String sort, String dir) {
		
		Building building = genericService.load(Building.class, buildingId);
		Room model = new Room();
		LandlordUser landlordUser = landlordUserService.getLoginUser().getOwnerUser();
		ModelUtil.setProperty(model, "landlordUser.id", landlordUser.getId());
		ModelUtil.setProperty(model, "building.id", buildingId);
		model.setStatus(Version.STATUS_VALID);
		if (!isAll) {
			ModelUtil.setProperty(model, "hasRent", hasRent);
		}
		
		PageInfo<Room> pageInfo = genericService.searchByModel(model, Condition.DEFAULT, false, false);
		List<Room> pageInfoRooms = sortAndPageList(pageInfo.getData(), sort, dir, start, limit);
		
		List<Room> rooms = new ArrayList<Room>();
		for (Room room : pageInfoRooms) {
			room.setHasRent(hasRent);
			List<FeeRule> feeRules = room.getFeeRules();
			
			Agreement lastAgreement = null;
			List<Agreement> agreements = room.getAgreements();
			if (agreements != null && agreements.size() > 0) {
				lastAgreement = agreements.get(0);
			}
			if (!room.getHasRent() && lastAgreement != null && lastAgreement.getFees().size() > 1) {
				room.setIsUndoThrowALease(true);
			}
			
			ValidAgreement agreement = room.getAgreement();
			if (agreement == null) {
				agreement = new ValidAgreement();
			}
			if (agreement.getRentType() == null) {
				agreement.setRentType(building.getRentPayMode());
				room.setAgreement(agreement);
			}
			
			
			List<FeeRule> fixedFeeRules = new ArrayList<FeeRule>();
			if (feeRules != null) {
				for (FeeRule feeRule : feeRules) {
					FeeRuleCfg feeRuleCfg = feeRule.getFeeRuleCfg();
					if (feeRuleCfg != null) {
						FeeRuleCfgDesc feeRuleCfgDesc = feeRuleCfg.getFeeRuleCfgDesc();
						if (feeRuleCfgDesc != null) {
							if (feeRuleCfgDesc.getType() == FeeRuleType.fixed) {
								fixedFeeRules.add(feeRule);
							}else if(feeRuleCfgDesc.getType() == FeeRuleType.meter){
								Meter curMeter = feeRule.getCurrentMeter();
								
								if (curMeter == null) {
									curMeter = new Meter();
									curMeter.setCount(BigDecimal.valueOf(0));
									curMeter.setFeeRule(feeRule);
									curMeter.setCheckTime(new Date());
									List<Meter> meters = new ArrayList<Meter>();
									meters.add(curMeter);
									feeRule.setMeters(meters);
								}
							}
						}
					}
					
				}
				//去除固定费用
				feeRules.removeAll(fixedFeeRules);
			}
			
			rooms.add(room);
		}
		return rooms;
	}
	
	/**
	 *  出租， 查询过期合同
	 * @param buildingId
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchWithRentalExpire(Integer buildingId) {
		List<Room> expireRooms = new ArrayList<>();
		List<Room> allRooms = searchWithRental(buildingId, true, false, 0, 0, null, null);
		for (Room room : allRooms) 
		{
			//过期
			if (room.getAgreement() != null && room.getAgreement().getExpire() == 0)
			{
				expireRooms.add(room);
			}
		}
		
		return expireRooms;
	}
	
	
	public Room _unRent(Room model) {
		Room dbRoom = genericService.load(model);
		if (dbRoom != null) {
			FeeRecord feeRecord = feeService.getOrGenerateFeeRecordIfNotExists(dbRoom.getAgreement());
			if (feeRecord == null || feeRecord.getDateBeginRecv() == null || !StringUtils.isNotBlank(feeRecord.getDateBeginRecv())) {
				dbRoom.setHasPay(true);
			} else {
				if (DateTimeUtil.StrToDate(feeRecord.getDateBeginRecv()).compareTo(new Date()) <= 0) {
					Fee receivableFee = new Fee();
					feeRecordService.copyFeeRecord2Fee(feeRecord, receivableFee);
					dbRoom.setReceivableFee(receivableFee);
					dbRoom.setHasPay(false);
				} else {
					//加入抄表费用
					if (feeRecord.getFeeAdditionRecords() != null) {
						Fee receivableFee = new Fee();
						receivableFee.setDateBeginRecv(DateTimeUtil.formatDate(new Date()));
						receivableFee.setDateEndRecv(DateTimeUtil.formatDate(new Date()));
						receivableFee.setFeeAdditions(new ArrayList<FeeAddition>());
						for (FeeAdditionRecord feeAdditionRecord : feeRecord.getFeeAdditionRecords()) {
							if (feeAdditionRecord.getFeeRuleCfg().getFeeRuleCfgDesc().getType() == FeeRuleType.meter) {
								FeeAddition feeAddition = new FeeAddition();
								ModelUtil.copy(feeAdditionRecord, feeAddition, true);
								receivableFee.getFeeAdditions().add(feeAddition);
							}
						}
						dbRoom.setReceivableFee(receivableFee);
					}
					dbRoom.setHasPay(true);
				}
			}
		}
		if (!dbRoom.getHasPay()) {
			if (dbRoom.getAgreement().getFees() != null && dbRoom.getAgreement().getFees().size() > 0 && dbRoom.getAgreement().getFees().get(0).getBalance() != null) {
				dbRoom.getReceivableFee().setPrevBalance(dbRoom.getAgreement().getFees().get(0).getBalance().setScale(2, RoundingMode.HALF_UP));
			} else {
				dbRoom.getReceivableFee().setPrevBalance(BigDecimal.valueOf(0).setScale(2, RoundingMode.HALF_UP));
			}
		}
		return dbRoom;
	}
	/**
	 * 查询可退租的房间列表
	 * @param buildingId
	 * @param hasRent
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchWithRent(Integer buildingId, boolean hasRent, int start, int limit, String sort, String dir) {
		
		LandlordUser landlordUser = landlordUserService.getLoginUser().getOwnerUser();
		
		Room model = new Room();
		ModelUtil.setProperty(model, "building.id", buildingId);
		ModelUtil.setProperty(model, "hasRent", hasRent);
		ModelUtil.setProperty(model, "landlordUser.id", landlordUser.getId());
		model.setStatus(Version.STATUS_VALID);
		
		PageInfo<Room> pageInfo = genericService.searchAll(model);
		List<Room> pageInfoRooms = sortAndPageList(pageInfo.getData(), sort, dir, start, limit);
		
		List<Room> rooms = new ArrayList<Room>();
		for (Room room : pageInfoRooms) {
			Agreement validAgreement = room.getAgreement();
			if (validAgreement == null) {
				continue;
			}
			List<Fee> fees = validAgreement.getFees();
			
			if (fees != null && fees.size() > 0) {
				for (Fee fee : fees) {
					if (fee != null && fee.getStatus() == LandlordConstant.FEE_STATE_CONFIRMED) {
						room.setLastFee(fee);
						break;
					}
				}
				
				if (validAgreement != null ) {
					Fee fee = fees.get(0);
					validAgreement.setBalance(fee.getBalance());
				}
			}
			
			
			room.setHasRent(hasRent);
			rooms.add(room);
		}
		
		return rooms;
	}
	
	/**
	 * 根据仪表查询房屋
	 * @param buildingId
	 * @param meterTypeId
	 * @return
	 */
	@Rest(type = "search")
	public List<RoomDBVO> searchWithMeter(Integer buildingId, Integer meterTypeId, int start, int limit, String sort, String dir) {
		// 已抄表: room.feeRule.meter
		// 未抄表
		RoomDBVO model = new RoomDBVO();
		LandlordUser landlordUser = landlordUserService.getLoginUser();
		model.setLandlordUser(landlordUser.getOwnerUser());
		ModelUtil.setProperty(model, "building.id", buildingId);
		model.setStatus(Version.STATUS_VALID);
		
		// 表类型
		PageInfo<RoomDBVO> pageInfo = genericService.search(model, 0, 1000, "a.buildingFloor.id,a.name", "asc,asc", false);
		List<RoomDBVO> pageInfoRooms = sortAndPageList(pageInfo.getData(), sort, dir, start, limit);
		
		int today = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
		List<RoomDBVO> rooms = new ArrayList<RoomDBVO>();
		for (RoomDBVO room : pageInfoRooms) {
			FeeRule matchedFeeRule = null;
			for (FeeRule feeRule : room.getFeeRules()) {
				if (feeRule != null && feeRule.getFeeRuleCfg() != null) {
					if (meterTypeId.equals(feeRule.getFeeRuleCfg().getFeeRuleCfgDesc().getId())) {
						matchedFeeRule = feeRule;
						break;
					}
				}
			}
			if (matchedFeeRule != null) {
				List<Meter> lstMeter = matchedFeeRule.getMeters();
				if (lstMeter == null || lstMeter.size() == 0) {
					//初始化表刻度
					Meter initMeter = new Meter();
					initMeter.setCount(BigDecimal.valueOf(0));
					initMeter.setFeeRule(matchedFeeRule);
					initMeter.setCheckTime(null);
					room.setLastMeter(initMeter);
				}else {
					room.setLastMeter(lstMeter.get(0));
					if (lstMeter.size() > 1) {
						room.setLastLastMeter(lstMeter.get(1));
					}
				}
				rooms.add(room);
			}
		}
		return rooms;
	}
	
	/**
	 * 查询某个微信用户绑定的房间的用水用电情况
	 * @param room
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> getCurAgreementFee() {
		//LoginUser loginUser = loginUserService.getLoginUser();
		return null;
	}
	
	/**
	 * 查询某个微信用户绑定的房间的用水用电情况
	 * @param room
	 * @return
	 */
	@Rest(type = "search")
	public String getRoomRenttypeDesc(Integer roomRenttype) {
		return LandlordConstant.ROOM_RENTTYPE_DESCRIBE.get(roomRenttype);
	}
	
	/**
	 * 查询房间(通过楼房ID查询房间)
	 * @param room
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchRooms(Integer buildingId) {
		Room model = new Room();
		ModelUtil.setProperty(model, "building.id", buildingId);
		return genericService.searchAll(model).getData();
	}
	
	/**
	 * 查询房间
	 * @param room
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchRoom(Room room, int start, int limit, String sort, String dir) {
		List<Room> rooms;
		if (room.getBuilding() != null && room.getBuilding().getId() != null) {
			// 更快的房间查找
			Building building = genericService.load(room.getBuilding(), false);
			genericService.refresh(building);
			rooms = building.getRooms();
			rooms = sortAndPageList(rooms, sort, dir, start, limit);
		} else {
			LandlordUser landlordUser = landlordUserService.getLoginUser();
			room.setLandlordUser(landlordUser.getOwnerUser());
			room.setStatus(Version.STATUS_VALID);
			PageInfo<Room> pageInfo = genericService.searchByModel(room, Condition.DEFAULT, false, false);
			rooms = sortAndPageList(pageInfo.getData(), sort, dir, start, limit);
		}
		return rooms;
	}
	
	/**
	 * 兼容iphone返回json的临时方案
	 * 查询房间
	 * @param room
	 * @return
	 */
	@Rest(type = "search")
	public List<Room> searchRoomNew(Room room, int start, int limit, String sort, String dir) {
		return searchRoom(room, start, limit, sort, dir);
	}
	
	@Rest(type = "search")
	@Json("id,name,roomType,roomTypeDesc,agreement.tenantUser.name")
	public List<Room> searchSimpleRoom(Room room, int start, int limit, String sort, String dir) {
		return searchRoom(room, start, limit, sort, dir);
	}
	
	@Rest(type = "search")
	public Room viewRoomDetail(Room model) {
		Room dbRoom = genericService.load(model, false);
		return dbRoom;
	}
	
	/**
	 * web端查询房间. 
	 * @param room 未缴费: room.setHasPay(false); 已缴费: room.setHasPay(true); 
	 * @return 未设置hasPay字段则返回所有状态的的房间
	 */
	@Rest(type = "search")
	public List<Room> searchBuildingsRoom(Room room, Integer expiredLeave, int start, int limit, String sort, String dir) {
		List<Room> lstRoom = searchRoom(room, start, limit, sort, dir);
		List<Room> lstPayRoom = new ArrayList<>();
		List<Room> lstNoPayRoom = new ArrayList<>();
		
		if (lstRoom != null) {
			String today=DateTimeUtil.formatDate(new Date());
			for (Room r: lstRoom) {
				Agreement validAgreement = r.getAgreement();
				if(validAgreement != null){
					List<Fee> lstFee = validAgreement.getFees();
					if (lstFee == null || lstFee.size() == 0) {
						//预定房间的也放在未收租里
						if (r.getRentState() == LandlordConstant.RENT_STATE_BOOK) {
							r.setHasPay(false);
							lstNoPayRoom.add(r);
						}
						continue;
					}else if(lstFee.size() == 1){
						r.setHasPay(false);
						lstNoPayRoom.add(r);
						continue;
					}else if(lstFee.size() > 1){
						if (lstFee.get(0).getDateEndRecv().compareTo(today) < 0) {
							r.setHasPay(false);
							lstNoPayRoom.add(r);
							continue;
						}
						r.setHasPay(true);
						lstPayRoom.add(r);
					}
					
				}
			}
			
			if (room.getHasPay() != null) {
				if(room.getHasPay()){
					lstRoom = lstPayRoom;
				}else{
					lstRoom = lstNoPayRoom;
				}
			}
		}
		List<Room> resultRoom = new ArrayList<Room>(lstRoom.size());
		if (expiredLeave != null) {
			Calendar now = Calendar.getInstance();
			if (expiredLeave !=null) {
				now.add(Calendar.DATE, expiredLeave);
			}
			SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			for (Room r : lstRoom) {
				Agreement validAgreement = r.getAgreement();
				if (validAgreement != null) {
					if (DateTimeUtil.StrToDate(validAgreement.getRentEndDate(), sdf).compareTo(now.getTime()) <= 0) {
						resultRoom.add(r);
					}
				}
			}
			return resultRoom;
		}
		return lstRoom;
	}
	/**
	 * 退租
	 * @param model
	 * @param isPublish 是否发布
	 * @return
	 * @throws ParseException 
	 */
	@Transactional
	public Room throwLease(ValidAgreement model, Boolean isPublish) {
		
		Agreement agreement = genericService.load(model,false);
		
		if (agreement == null) {
			throw new ApplicationException("该房间已退租，无法进行再次退租操作！");
		}
		
		Room room = agreement.getRoom();
		List<Fee> fees = agreement.getFees();
		
		//首先查询是否有费用未结清，如有，抛异常，需结清费用后再退租
		if (fees == null) {
			fees = new ArrayList<Fee>();
			agreement.setFees(fees);
		}
		if (fees != null && fees.size() > 0) {
			Fee throwLeaseFee = new Fee();
			throwLeaseFee.setRoom(room);
			throwLeaseFee.setAmountRecv(new BigDecimal(model.getReturnFee()).multiply(BigDecimal.valueOf(-1)).setScale(2,BigDecimal.ROUND_HALF_UP));
			throwLeaseFee.setAmountActual(new BigDecimal(model.getReturnFee()).multiply(BigDecimal.valueOf(-1)).setScale(2,BigDecimal.ROUND_HALF_UP));
			throwLeaseFee.setBalance(BigDecimal.valueOf(0).setScale(2,BigDecimal.ROUND_HALF_UP));
			throwLeaseFee.setRental(BigDecimal.valueOf(0).setScale(2,BigDecimal.ROUND_HALF_UP));
			throwLeaseFee.setDateActual(DateTimeUtil.DATETIME_FORMAT.format(Calendar.getInstance().getTime()));
			throwLeaseFee.setDateBeginRecv(DateTimeUtil.DATETIME_FORMAT.format(Calendar.getInstance().getTime()));
			throwLeaseFee.setDateEndRecv(DateTimeUtil.DATETIME_FORMAT.format(Calendar.getInstance().getTime()));
			throwLeaseFee.setTenantUser(agreement.getTenantUser());
			throwLeaseFee.setStatus(LandlordConstant.FEE_STATE_CONFIRMED);
			throwLeaseFee.setType(1);
			throwLeaseFee.setAgreement(agreement);
			throwLeaseFee.setAgent(landlordUserService.getLoginUser());
			throwLeaseFee.setLandlordUser(landlordUserService.getLoginUser().getOwnerUser());
			throwLeaseFee.setRemark(model.getRemark());
			throwLeaseFee.setFeeType(Fee.FEE_TYPE_UNREANTAL);
			fees.add(throwLeaseFee);
		}
		agreement.setRentLeaveDate(DateTimeUtil.completeDateTime(model.getRentLeaveDate()));
		agreement.setReturnFee(model.getReturnFee());
		agreement.setRemark("退房");
		
		genericService.update(agreement);
		genericService.refresh(agreement);
		if (agreement.getFees() != null && agreement.getFees().size() > 0) {
			financeStatementService.autoCreateFinanceStatement(agreement.getFees().get(0));
		}
		
		//退租：将出租状态设为1
		room.setRentState(LandlordConstant.RENT_STATE_NOYET);
		
		Room resultRoom = genericService.merge(room);
		
		//将TenantRoomRelation的relation_type_ext修改为1，表示关系无效
		TenantRoomRelation tenantRoomRelation = new TenantRoomRelation();
		tenantRoomRelation.setAgreement(agreement);
		List<TenantRoomRelation> tenantRoomRelations = genericService.searchAll(tenantRoomRelation).getData();
		if (tenantRoomRelations != null) {
			for (TenantRoomRelation tenantRoomRelation2 : tenantRoomRelations) {
				if(tenantRoomRelation2.getExtType() == 0){
					tenantRoomRelation2.setExtType(1);
					genericService.update(tenantRoomRelation2);
				}
			}
		}
				
		genericService.executeAsync(new Executor<Boolean, Agreement>() {
			@Override
			public Boolean execute(Agreement agreement) {
				genericService.changeModelType(agreement, InvalidAgreement.class);
				return Boolean.TRUE;
			}
		}, agreement);
		
		final Boolean publish = isPublish;
		//同步信息和发布状态
		genericService.executeAsync(new Executor<Boolean, Room>() {
			@Override
			public Boolean execute(Room room) {
				//延时1秒，临时方案
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					logger.error("wait 1 second", e);
				}
				
				//退租后，如果需要推广，则自动发布推广
				room = genericService.load(room);
				if (room.getBuilding() != null) {
					
					boolean isPub = true;
					if (Boolean.FALSE.equals(publish))
					{
						isPub = false;
					}
					retrievalService.publish(room, isPub);
				}
				return Boolean.TRUE;
			}
		}, room);
		
		//将未收租的费用设置为已收租，删除订单，是否还需要填补fee?
		feeRecordService.finishFeeRecordAndOrderByRoom(resultRoom);
		
		//--根据信息定时发送设定判断是否需要刷新
		notificationService.indicateNotificationNeedRefresh();
		
		return resultRoom;
	}
	
	
	private boolean effectiveTenantRoomRelation(Agreement agreement){
		boolean result = false;
		TenantRoomRelation model = new TenantRoomRelation();
		model.setAgreement(agreement);
		List<TenantRoomRelation> tenantRoomRelations = genericService.searchAll(model).getData();
		if (tenantRoomRelations != null) {
			for (TenantRoomRelation tenantRoomRelation : tenantRoomRelations) {
				if(tenantRoomRelation.getExtType() == 1){
					tenantRoomRelation.setExtType(0);
					genericService.merge(tenantRoomRelation);
				}
			}
			result = true;
		}
		return result;
	}

	
	/**
	 * 撤销退租
	 * @param model
	 * @return
	 */
	public Room undoThrowLease(Room model) {
		Room room = genericService.load(model, false);
		if (room == null) {
			throw new ApplicationException("未找到对应的房间或无权限对该房间进行撤销退租操作！");
		}
		room.setRentState(LandlordConstant.RENT_STATE_ALREADY);
		genericService.merge(room);
		
		List<Agreement> agreements = room.getAgreements();
		if (agreements == null || agreements.size() == 0) {
			throw new ApplicationException("该房间未被出租过，不能进行撤销退租操作！");
		}
		Agreement lastAgreement = agreements.get(0);
		List<Fee> fees = lastAgreement.getFees();
		if (fees.size() > 1) {
			fees.remove(0);
		}
		lastAgreement.setRentLeaveDate(null);
		lastAgreement.setReturnFee(null);
		genericService.merge(lastAgreement);
		effectiveTenantRoomRelation(lastAgreement);
		
		genericService.executeAsync(new Executor<Agreement, Agreement>() {
			@Override
			public Agreement execute(Agreement lastAgreement) {
				Agreement agreement = genericService.load(lastAgreement);
				return genericService.changeModelType(agreement, ValidAgreement.class);
			}
		}, lastAgreement);
		return room;
	}

	
	// Job（已废）
	@SuppressWarnings("unchecked")
	public void updateRoomFee() {
		String ql = "from " + Room.class.getName() + " a where a.agreement is not null";
		List<Room> rooms = genericService.searchByQl(new QLInfo(ql, true)).getData();
		Iterator<Room> iter = rooms.iterator();
		while (iter.hasNext()) {
			updateSingleRoomFee(iter.next(), true);
		}
	}
	
	/**
	 * 获取房东总数
	 * 
	 * @return 房东总数
	 */
	@Rest(simpleServiceName = "anonymity", type = "free")
	public int getLandlordUserCount() {
		return 2000 + retrievalService.getLandlordUserCount();
	}
	
	/**
	 * 获取房源总数
	 * 
	 * @return 房源总数
	 */
	@Rest(simpleServiceName = "anonymity", type = "free")
	public int getRoomCount() {
		return retrievalService.getRoomCount();
	}
	
	@SuppressWarnings("unchecked")
	public List<Room> searchAllBuildingsRoom (Integer chargeState) {
		Session session = (Session)em.getDelegate();
		QLInfo ql = new QLInfo();
		StringBuilder sb = new StringBuilder();
		sb.append("select a from Room a left join a.agreements b with b.agreementState = 0 where a.landlordUser = ? ");
		ql.addParam(landlordUserService.getLandlordOwnerUser());
		
		sb.append(" and exists (from a.building.landlordUsers e where e.id = ?)");
		ql.addParam(landlordUserService.getLandlordUser().getId());
		
		sb.append(" and ( (a.rentType = ? and a.rentState = ?) or a.rentType = ?) and b is not null");
		ql.addParam(Room.RENT_TYPE_ENTIRE);
		ql.addParam(Room.RENT_STATE_ALREADY);
		ql.addParam(Room.RENT_TYPE_SUB);
		Filter filter = session.enableFilter("rentStateFilter");
		filter.setParameter("rentState", Room.RENT_STATE_ALREADY);
		
		if (chargeState != null) {
			//TODO 给subroom添加对应的两个filter
			if (chargeState == 0) { //已缴费
				sb.append(" and b is not null and exists (select c.id from b.fees c where substr(date_add_interval(c.dateEndRecv, -ifnull(b.advanceNum, 3)), 1, 10) >= substr(?, 1, 10) and c.feeType = ? ))");
				ql.addParam(DateTimeUtil.throwTimeForDate(new Date()));
				ql.addParam(Fee.FEE_TYPE_RENTAL);
			} else if (chargeState == 1) { //未缴费
				sb.append(" and b is not null and a.rentState=? and not exists (select c.id from b.fees c where substr(date_add_interval(c.dateEndRecv, -ifnull(b.advanceNum, 3)), 1, 10) >= substr(?, 1, 10) and c.feeType = ? ))");
				ql.addParam(Room.RENT_STATE_ALREADY);
				ql.addParam(DateTimeUtil.throwTimeForDate(new Date()));
				ql.addParam(Fee.FEE_TYPE_RENTAL);
			}
		}
		
		ql.setQl(sb.toString());
		ql.setDisablePermissionFilter(false);
		return genericService.searchByQl(ql).getData();
	}
	
	@SuppressWarnings("unchecked")
	public PageInfo<Room> searchRoomsNew (Room model, String tenantUserName, Integer chargeState, Integer expiredLeave) {
		Building building = genericService.load(Building.class, model.getBuilding().getId());
		ActionContext.getContext().getValueStack().set("building", building);
		Session session = (Session)em.getDelegate();
		QLInfo ql = new QLInfo();
		StringBuilder sb = new StringBuilder();
		sb.append("select a from Room a left join a.agreements b with b.agreementState = 0 where a.building.id = ? ");
		ql.addParam(model.getBuilding().getId());
		if (model.getBuildingFloor() != null && model.getBuildingFloor().getId() != null) {
			if (model.getBuildingFloor().getId() != 0) {
				sb.append(" and a.buildingFloor.id = ?");
				ql.addParam(model.getBuildingFloor().getId());	
			} 
		} else {
			sb.append(" and a.buildingFloor.id = ?");
			ql.addParam(building.getBuildingFloors().get(0).getId());
		}
		if (StringUtils.isNotEmpty(model.getName())) {
			sb.append(" and a.name like ? ");
			ql.addParam("%" + model.getName().trim() + "%");
		}
		if (StringUtils.isNotEmpty(tenantUserName)) {
			sb.append(" and ( (a.rentType = ? and b is not null and b.tenantUser.name like ?) or a.rentType=? )");
			ql.addParam(Room.RENT_TYPE_ENTIRE);
			ql.addParam("%" + tenantUserName + "%");
			ql.addParam(Room.RENT_TYPE_SUB);
			Filter filter = session.enableFilter("tenantUserNameFilter");
			filter.setParameter("tenantUserName", "%" + tenantUserName + "%");
		}
		if (model.getRentType() != null) {
			sb.append(" and a.rentType = ?");
			ql.addParam(model.getRentType());
		}
		if (model.getRentState() != null) {
			if (Room.RENT_STATE_ALREADY.equals(model.getRentState())) {
				sb.append(" and ( (a.rentType = ? and a.rentState = ?) or a.rentType = ?) and b is not null");
			} else {
				sb.append(" and ( (a.rentType = ? and a.rentState = ?) or a.rentType = ? or b is null)");
			}
			ql.addParam(Room.RENT_TYPE_ENTIRE);
			ql.addParam(model.getRentState());
			ql.addParam(Room.RENT_TYPE_SUB);
			Filter filter = session.enableFilter("rentStateFilter");
			filter.setParameter("rentState", model.getRentState());
		}
		if (chargeState != null) {
			//TODO 给subroom添加对应的两个filter
			if (chargeState == 0) { //已缴费
				sb.append(" and b is not null and exists (select c.id from b.fees c where substr(date_add_interval(c.dateEndRecv, -ifnull(b.advanceNum, 3)), 1, 10) >= substr(?, 1, 10) and c.feeType = ? ))");
				ql.addParam(DateTimeUtil.throwTimeForDate(new Date()));
				ql.addParam(Fee.FEE_TYPE_RENTAL);
			} else if (chargeState == 1) { //未缴费
				sb.append(" and b is not null and a.rentState=? and not exists (select c.id from b.fees c where substr(date_add_interval(c.dateEndRecv, -ifnull(b.advanceNum, 3)), 1, 10) >= substr(?, 1, 10) and c.feeType = ? ))");
				ql.addParam(Room.RENT_STATE_ALREADY);
				ql.addParam(DateTimeUtil.throwTimeForDate(new Date()));
				ql.addParam(Fee.FEE_TYPE_RENTAL);
			}
		}
		if (expiredLeave != null) {
			Calendar now = Calendar.getInstance();
			if (expiredLeave !=null) {
				now.add(Calendar.DATE, expiredLeave);
			}
			sb.append(" and b is not null and b.rentEndDate <= ?");
			ql.addParam(DateTimeUtil.formatDate(now.getTime()));
			/*Filter filter = session.enableFilter("rentEndDateFilter");
			filter.setParameter("rentEndDate", now.getTime());*/
		}
		ql.setQl(sb.toString());
		ql.setDisablePermissionFilter(false);
		ql.setCompiledSort("a.buildingFloor.id, a.name");
		ql.setDir("asc, asc");
		PageInfo<Room> pageInfo = genericService.searchByQl(ql);
		for(Room room : pageInfo.getData()) {
			setRoomHasPay(room);
			if ( (Room.RENT_STATE_ALREADY.equals(room.getRentState()) || Room.RENT_STATE_BOOK.equals(room.getRentState())) && room.getAgreement() != null ) {
				room.setTenantUserName(room.getAgreement().getTenantUser().getName());
			}
		}
		return pageInfo;
	}
	
	public void setRoomHasPay(Room dbRoom) {
		Calendar now = Calendar.getInstance();
		if (dbRoom.getRentType() == Room.RENT_TYPE_ENTIRE) {
			if (dbRoom.getAgreement() != null) {
				if (dbRoom.getAgreement().getId() == null || dbRoom.getAgreement().getFees().size() <= 1 || DateTimeUtil.GetDateWithAdvanceNum(DateTimeUtil.StrToDate(dbRoom.getAgreement().getFees().get(0).getDateEndRecv()), dbRoom.getAgreement().getAdvanceNum()).compareTo(DateTimeUtil.throwTimeForDate(new Date())) < 0) {
					dbRoom.setHasPay(false);
				} else {
					dbRoom.setHasPay(true);
				}
			} else {
				dbRoom.setHasPay(false);
			}
		} else {
			//TODO: subRoom
		}
	}
	/**
	 * 为APP而生
	 * @param model
	 * @param tenantUserName
	 * @param chargeState
	 * @param expiredLeave
	 * @return
	 */
	public List<Room> searchRoomsForApp (Room model, String tenantUserName, Integer chargeState, Integer expiredLeave) {
		return searchRoomsNew(model, tenantUserName, chargeState, expiredLeave).getData();
	}
	
	public PageInfo<Room> searchRoomsInList (Room model, String tenantUserName, Integer chargeState, Integer expiredLeave) {
		return searchRoomsNew(model, tenantUserName, chargeState, expiredLeave);
	}
	
	public PageInfo<Room> searchRoomsInGraphic (Room model, String tenantUserName, Integer chargeState, Integer expiredLeave) {
		return searchRoomsNew(model, tenantUserName, chargeState, expiredLeave);
	}
	
	public Room _rent(Room model) {
		return genericService.load(model);
	}
	
	public Agreement rent(ValidAgreement model, List<Meter> meters) {
		return agreementService.addRentAgreement(model, meters);
	}
	
	public Room _book(Room model) {
		return genericService.load(model);
	}
	
	public Agreement book(ValidAgreement model) {
		return agreementService.addBookAgreement(model);
	}
	
	public Room _collectRental(Room model) {
		Room dbRoom = genericService.load(model);
		Fee receivableFee = new Fee();
		FeeRecord example = new FeeRecord();
		FeeRecord feeRecord = null;
		
		if (model.getHasPay() != null && model.getHasPay()) {
			receivableFee = model.getAgreement().getFees().get(0);
		}else{
			example.setAgreement(dbRoom.getAgreement());
			//example.setStatus(FeeRecord.FEE_STATUS_RENT_TODO);
			
			Condition condition = new Condition();
			condition.setSort("id");
			condition.setDir("desc");
			condition.setSize(1);
			List<FeeRecord> feeRecords = genericService.searchByModel(example, condition, false, false).getData();
			if (feeRecords == null || feeRecords.size() == 0 || FeeRecord.FEE_STATUS_RENT_TODO != feeRecords.get(0).getStatus()) {//fee_status_rent_todo是int类型
				feeRecord = feeService.generateFeeRecord(dbRoom);
			} else {
				feeRecord = feeService.repaireFeeRecords(feeRecords);
			}
			feeRecordService.copyFeeRecord2Fee(feeRecord, receivableFee);
			receivableFee.setId(null);
			receivableFee.setFeeRecordId(feeRecord.getId());
		}
		
		List<FeeAddition> feeAdditions = receivableFee.getFeeAdditions();
		if(feeAdditions!=null){
			for (FeeAddition addition : feeAdditions) {
				FeeRuleCfg ruleCfg = addition.getFeeRuleCfg();
				FeeRuleCfgDesc feeRuleCfgDesc = ruleCfg.getFeeRuleCfgDesc();
				String name = feeRuleCfgDesc.getName();
				String feeAddtionName = addition.getName();
				if(StringUtils.isBlank(feeAddtionName)){
					addition.setName(name);
				}
			}
		}
		
		
		dbRoom.setReceivableFee(receivableFee);
		
		if (dbRoom.getAgreement().getFees() == null || dbRoom.getAgreement().getFees().size() == 0) {
			dbRoom.setNeedAddDeposit(true);
		} else {
			dbRoom.getReceivableFee().setPrevBalance(dbRoom.getAgreement().getFees().get(0).getBalance());
		}
		if (dbRoom.getAgreement().getFees() == null || dbRoom.getAgreement().getFees().size() <= 1) {
			dbRoom.getReceivableFee().setCanChangeBeginDate(true);
		} else {
			dbRoom.getReceivableFee().setCanChangeBeginDate(false);
		}
		
		//设置是否有网上收租单、上传的收租凭证、支付方式
		if (receivableFee != null && feeRecord != null && feeRecord.getId() != null) {
			//获取订单中的paymode,RentalVoucherFileName
			RentalOrder rentalOrder = feeRecordService.searchRentalOrderByFee(feeRecord);
			if (rentalOrder != null) {
				if (receivableFee.getPayMode() == null) {
					receivableFee.setPayMode(rentalOrder.getRentalVoucherPayType());
				}
				receivableFee.setRentalVoucherFileName(rentalOrder.getRentalVoucherFileName());
				receivableFee.setHasRentalOrder(true);
			}else{
				receivableFee.setHasRentalOrder(false);
			}
			receivableFee.setFeeRecordId(feeRecord.getId());
		}
		
		//设置上次费用
		if (dbRoom.getLastFee() == null) {
			List<Fee> fees = dbRoom.getAgreement().getFees();
			if (fees != null && fees.size() > 1) {
				boolean hasPay = dbRoom.getHasPay() != null && dbRoom.getHasPay();
				if(hasPay && fees.size() > 2){
					dbRoom.setLastFee(fees.get(1));
				}
				if(!hasPay){
					dbRoom.setLastFee(fees.get(0));
				}
			}
		}

		if (receivableFee.getRentalCalc() == null) {
			//设置计算应收房租
			int monthCycle = dbRoom.getAgreement().getRentCycle() + 1;
			if (dbRoom.getAgreement().getFees() == null || dbRoom.getAgreement().getFees().size() <= 1) {
				if (dbRoom.getAgreement().getPayNum() != null) {
					monthCycle = dbRoom.getAgreement().getPayNum();
				}
			}
			receivableFee.setRentalCalc(dbRoom.getAgreement().getRentPay());
			receivableFee.setRentalCalc(receivableFee.getRentalCalc().multiply(BigDecimal.valueOf(monthCycle)).setScale(2, RoundingMode.HALF_UP));
		}
		return dbRoom;
	}
	
	public Fee collectRental(Fee model, List<FeeAddition> extraFee, BigDecimal depositRecv, BigDecimal depositActual) {
		return feeService.collectRental(model, extraFee, depositRecv, depositActual);
	}
	
	public void adaptRoomConfig() {
		logger.info("### Begin to adaptRoomConfig ###");
		
		QLInfo ql = new QLInfo("select a from " + Room.class.getName() + " a where a.roomConfig is null and a.config != 0", true);
		int count = genericService.getCount(ql);
		for (int i = 0; i < count; i += 1000) {
			ql.setFirstResult(0); 		//下次查，已经修正的数据已被修正，所以还是得从0开始
			ql.setMaxResults(1000);
			List<Room> rooms = genericService.searchByQl(ql).getData();
			if (rooms != null && rooms.size() > 0) {
				this.getThis().adaptRoomConfigInterval(rooms);
			}
		}
		
		logger.info("### End adaptRoomConfig ###");
	}
	
	@Transactional
	public void adaptRoomConfigInterval(List<Room> rooms) {
		for (Room room : rooms) {
			try{
				fillRoomConfig(room);
			} catch(Exception e) {
				logger.error("Fail to correct roomConfig: roomId:{" + room.getId() + "}", e);
			}
		}
		em.flush();
		em.clear();
		logger.info("repaire roomconfig success (1000 rooms), the lastest room id :" + rooms.get(rooms.size() -1 ).getId());
	}
	
	public void adaptRoomRentType() {
		logger.info("### Begin to adaptRoomRentType ###");
		
		QLInfo ql = new QLInfo("select a from " + Room.class.getName() + " a where a.roomCount is null", true);
		int count = genericService.getCount(ql);
		for (int i = 0; i < count; i += 1000) {
			ql.setFirstResult(i);
			ql.setMaxResults(1000);
			List<Room> rooms = genericService.searchByQl(ql).getData();
			if (rooms != null && rooms.size() > 0) {
				this.getThis().adaptRoomRentTypeInterval(rooms);
			}
		}
		logger.info("### End adaptRoomRentType ###");
	}
	
	@Transactional
	public void adaptRoomRentTypeInterval(List<Room> rooms) {
		for (Room room : rooms) {
			try{
				room.adaptRoomType();
			} catch(Exception e) {
				logger.error("Fail to correct roomConfig: roomId:{" + room.getId() + "}", e);
			}
		}
		em.flush();
		em.clear();
		logger.info("repaire roomType success (1000 rooms), the lastest room id :" + rooms.get(rooms.size() -1 ).getId());
	}
	
	public void adaptAgreementRentPeriod() {
		logger.info("### Begin to adaptAgreementRentPeriod ###");
		
		QLInfo ql = new QLInfo("select a from " + Agreement.class.getName() + " a where a.agreementState = ? and (a.tenancy is null or a.depositNum is null) and a.room.status = 1", true);
		ql.addParam(Agreement.AGREEMENT_STATE_VALID);
		int count = genericService.getCount(ql);
		for (int i = 0; i < count; i += 1000) {
			ql.setFirstResult(0);
			ql.setMaxResults(1000);
			List<Agreement> agreements = genericService.searchByQl(ql).getData();
			if (agreements != null && agreements.size() > 0) {
				this.getThis().adaptAgreementRentPeriodInterval(agreements);
			}
		}
		logger.info("### End adaptAgreementRentPeriod ###");
	}
	
	@Transactional
	public void adaptAgreementRentPeriodInterval(List<Agreement> agreements) {
		for (Agreement agreement : agreements) {
			try{
				agreement.adaptRentPeriod();
				agreement.adaptRentType();
			} catch(Exception e) {
				logger.error("Fail to correct rentperiod and renttype of agreemnt: agreemntId:{" + agreement.getId() + "}", e);
			}
		}
		em.flush();
		em.clear();
		logger.info("repaire Agreement rentPeriod and rentType success (1000 agreements), the lastest agreement id :" + agreements.get(agreements.size() -1 ).getId());
	}
	
	public void adaptForVC() {
		
		logger.info("### Begin to adaptForVC data ###");
		long beginTime = System.currentTimeMillis();
		
		adaptRoomConfig();
		adaptRoomRentType();
		adaptAgreementRentPeriod();
		
		long consumedTime = (System.currentTimeMillis() - beginTime) / 1000;
		logger.info("### End adaptForVC data consumed time: " + consumedTime + "'s ###");

		taskSchedulerService.updateTaskSchedulerRunTime(this.getClass(), "adaptForVC");
	}
	
	/**
	 * 根据楼栋修复下面所有房间的  待收费（固定费用）
	 * @param model
	 */
	public void repaireFixedFeeRecord(Building model) {
		Building dbBuilding = genericService.load(model);
		for (Room room : dbBuilding.getRooms()) {
			try {
				updateRoom(room);
			} catch (Exception e) {
				logger.error("修复固定费用到待收费失败, room:" + room.getId() + ", " + room.getName(), e);
			}
		}
	}
	
	/**
	 * 根据楼栋修复下面所有房间的房租为合约的房租（固定费用）
	 * @param model
	 */
	public boolean setRoomRentalFromAgreement(Building model, Integer roomId) {
		Building dbBuilding = genericService.load(model);
		for (Room room : dbBuilding.getRooms()) {
			try {
				if (room.getAgreement() != null && room.getAgreement().getRentPay() != null) {
					room.setRental(room.getAgreement().getRentPay().toString());
					if (roomId != null && !room.getId().equals(roomId)) {
						continue;
					}
					genericService.merge(room);
				}
			} catch (Exception e) {
				logger.error("修复固定费用到待收费失败, room:" + room.getId() + ", " + room.getName(), e);
			}
		}
		return true;
	}
	
	/**
	 * App批量建楼后调用此接口批量修改房间费用
	 * @param buildingId
	 * @param feeRuleCfgs
	 * @return
	 */
	@Transactional(propagation = Propagation.SUPPORTS)
	public boolean batchUpdateBuildingRoomsFeeRule(Integer buildingId, List<FeeRuleCfg> feeRuleCfgs) {
		Building dbBuilding = genericService.load(Building.class, buildingId);
		if (dbBuilding == null) {
			throw new ApplicationException("楼栋找不到！");
		}
		batchUpdateRoomsFeeRuleInterval(dbBuilding.getRooms(), feeRuleCfgs);
		return true;
	}
	
	/**
	 * App批量修改房间的费用
	 * @param roomIds
	 * @param feeRuleCfgs
	 * @return
	 */
	@Transactional(propagation = Propagation.SUPPORTS)
	public boolean batchUpdateRoomsFeeRule(String roomIds, List<FeeRuleCfg> feeRuleCfgs) {
		Assert.notBlank(roomIds, "没有选择任何房间");
		String [] _roomIds = roomIds.split(",");
		List <Room> rooms = new ArrayList<Room>(_roomIds.length);
		for (String roomId : _roomIds) {
			Room dbRoom = em.getReference(Room.class, Integer.parseInt(roomId));
			rooms.add(dbRoom);
		}
		batchUpdateRoomsFeeRuleInterval(rooms, feeRuleCfgs);
		return true;
	}
	
	@Transactional
	public void batchUpdateRoomsFeeRuleInterval(List<Room> dbRooms, List<FeeRuleCfg> feeRuleCfgs) {
		if (feeRuleCfgs == null || feeRuleCfgs.size() == 0) {
			if (dbRooms != null) {
				for (Room room : dbRooms) {
					room.getFeeRules().clear();
					em.merge(room);
				}
			}
		} else {
			List<FeeRuleCfg> dbFeeRuleCfgs = new ArrayList<FeeRuleCfg>(feeRuleCfgs.size());
			for (FeeRuleCfg feeRuleCfg : feeRuleCfgs) {
				dbFeeRuleCfgs.add(em.getReference(FeeRuleCfg.class, feeRuleCfg.getId()));
			}
			if (dbRooms != null) {
				for (Room room : dbRooms) {
					List<FeeRule> feeRules = new ArrayList<FeeRule>(feeRuleCfgs.size());
					for (FeeRuleCfg feeRuleCfg : dbFeeRuleCfgs) {
						FeeRule matchedFeeRule = null;
						if (feeRules != null) {
							for (FeeRule feeRule : room.getFeeRules()) {
								if (feeRule.getFeeRuleCfg().getId().equals(feeRuleCfg.getId())) {
									matchedFeeRule = feeRule;
									break;
								}
							}
							if (matchedFeeRule != null) {
								room.getFeeRules().add(matchedFeeRule);
								feeRules.add(matchedFeeRule);
							} else {
								FeeRule feeRule = new FeeRule();
								feeRule.setFeeRuleCfg(feeRuleCfg);
								feeRule.setLandlordUser(landlordUserService.getLandlordOwnerUser());
								feeRule.setStatus(Version.STATUS_VALID);
								feeRule.setRoom(room);
								em.persist(feeRule);
								feeRules.add(feeRule);
							}
						}
					}
					room.getFeeRules().clear();
					room.getFeeRules().addAll(feeRules);
					em.merge(room);
				}
			}
		}
		em.flush();
		em.clear();
		genericService.executeAsync(new Executor<Boolean, List<Room>>() {
			@Override
			public Boolean execute(List<Room> rooms) {
				for (Room room : rooms) {
					feeRecordService.repairedFeeRecordByRoom(room);
				}
				return Boolean.TRUE;
			}
		}, dbRooms);
	}
}